package com.idea.plugin.core.helper;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaCodeReferenceElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMember;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiModifierListOwner;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiParameterList;
import com.intellij.psi.PsiType;
import com.intellij.psi.impl.source.PsiJavaFileImpl;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

/**
 * @author yanzhao
 * @date 2022/2/24 17:52
 * @since 1.0.0
 */
public class JavaClassHelper {

    private static Project project = ProjectManager.getInstance().getOpenProjects()[0];

    private static GlobalSearchScope searchScope = GlobalSearchScope.allScope(project);

    private static JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project);

    private static PsiManager psiManager = PsiManager.getInstance(project);

    public static PsiClass getPsiClassByQualifiedName(String qualifiedName) {
        PsiClass psiClass = javaPsiFacade.findClass(qualifiedName, searchScope);
        return psiClass;
    }

    /**
     * @param psiClass
     * @return
     */
    public static boolean isAbstract(PsiClass psiClass) {
        return PsiUtil.isAbstractClass(psiClass);
    }

    /**
     * @param psiClass
     * @return
     */
    public static String getPackageName(PsiClass psiClass) {
        return PsiUtil.getPackageName(psiClass);
    }

    /**
     * 获取类的名称（无包）
     *
     * @param psiClass
     * @return
     */
    public static String getTypeName(PsiClass psiClass) {
        return psiClass.getName();
    }

    /**
     * 获取类的全限定名
     *
     * @param psiClass
     * @return
     */
    public static String getQualifiedName(PsiClass psiClass) {
        return psiClass.getQualifiedName();
    }

    /**
     * 获取类的全限定名
     *
     * @param psiParameter
     * @return
     */
    public static String getParameterQualifiedName(PsiParameter psiParameter) {
        return psiParameter.getType().getCanonicalText();
    }

    /**
     * 获取属性的类定义
     *
     * @param psiField
     * @return
     */
    public static PsiClass getPsiClassOfPsiField(PsiField psiField) {
        final String qualifiedName = psiField.getType().getCanonicalText();
        PsiClass psiClass = javaPsiFacade.findClass(qualifiedName, searchScope);
        return psiClass;
    }

    public static PsiClass findClassByQualifiedName(String qualifiedName) {
        PsiClass psiClass = javaPsiFacade.findClass(qualifiedName, searchScope);
        return psiClass;
    }

    public static PsiClass findSuperClassByQualifiedName(String qualifiedName) {
        PsiClass psiClass = javaPsiFacade.findClass(qualifiedName, searchScope);
        if (psiClass != null) {
            PsiClass superClass = psiClass.getSuperClass();
            return superClass;
        }
        return null;
    }

    public static PsiField[] getJavaFields(PsiClass psiClass) {
        return psiClass.getFields();
    }

    /**
     * 获取属性的全限定名
     *
     * @param psiField
     * @return
     */
    public static String getFieldQualifiedName(PsiField psiField) {
        return psiField.getType().getCanonicalText();
    }

    /**
     * @param psiModifierListOwner
     * @return
     */
    public static boolean isStatic(PsiModifierListOwner psiModifierListOwner) {
        return psiModifierListOwner.hasModifierProperty(PsiModifier.STATIC);
    }

    /**
     * @param psiModifierListOwner
     * @return
     */
    public static boolean isFinal(PsiModifierListOwner psiModifierListOwner) {
        return psiModifierListOwner.hasModifierProperty(PsiModifier.FINAL);
    }

    /**
     * 获取类的父类
     *
     * @param psiClass
     * @return
     */
    public static PsiClass getSuperClass(PsiClass psiClass) {
        return psiClass.getSuperClass();
    }

    /**
     * 获取属性的父类
     *
     * @param psiField
     * @return
     */
    public static PsiClass getSuperClassOfField(PsiField psiField) {
        PsiClass psiClass = getPsiClassOfPsiField(psiField);
        if (psiClass == null) {
            return null;
        }
        return getSuperClass(psiClass);
    }

    /**
     * 获取方法
     *
     * @param psiClass
     * @return
     */
    public static PsiMethod[] getPsiMethods(PsiClass psiClass) {
        return psiClass.getMethods();
    }

    public static PsiMethod[] getAllPsiMethods(PsiClass psiClass) {
        return psiClass.getAllMethods();
    }

    /**
     * 获取实现的接口
     *
     * @param psiClass
     * @return
     */
    public static PsiClass[] getInterfaces(PsiClass psiClass) {
        return psiClass.getInterfaces();
    }

    /**
     * 获取方法的参数
     *
     * @param psiMethod
     * @return
     */
    public static PsiParameter[] getParameters(PsiMethod psiMethod) {
        final PsiParameterList parameterList = psiMethod.getParameterList();
        return parameterList.getParameters();
    }

    /**
     * 获取返回值的全限定名称
     *
     * @param psiMethod
     * @return
     */
    public static String getReturnTypeQualifiedName(PsiMethod psiMethod) {
        final PsiType returnType = psiMethod.getReturnType();
        String canonicalText = returnType.getCanonicalText();
        if (canonicalText == null) {
            System.out.println();
        }
        return canonicalText;
    }

    /**
     * 判断元素是否私有
     *
     * @param psiModifierListOwner
     * @return
     */
    public static boolean isPrivate(PsiModifierListOwner psiModifierListOwner) {
        return psiModifierListOwner.hasModifierProperty(PsiModifier.PRIVATE);
    }

    public static boolean isPublic(PsiModifierListOwner psiModifierListOwner) {
        return psiModifierListOwner.hasModifierProperty(PsiModifier.PUBLIC);
    }

    /**
     * 判断类是否是抽象或接口
     *
     * @param psiClass
     * @return
     */
    public static boolean isInterfaceOrAbstract(PsiClass psiClass) {
        return psiClass.isInterface() || isAbstract(psiClass);
    }

    /**
     * 判断是否有默认的构造器
     *
     * @param psiClass
     * @return
     */
    public static boolean hasDefaultConstructor(PsiClass psiClass) {
        return PsiUtil.hasDefaultConstructor(psiClass);
    }

    /**
     * 类是否包含了指定注解
     *
     * @param psiClass
     * @param qualifiedName
     * @return
     */
    public static boolean hasAnnotation(PsiClass psiClass, String qualifiedName) {
        PsiAnnotation annotation = psiClass.getAnnotation(qualifiedName);
        return annotation == null;
    }

    /**
     * 获取方法抛出的异常
     *
     * @param psiMethod
     * @return
     */
    public static PsiJavaCodeReferenceElement[] getMethodThrowsException(PsiMethod psiMethod) {
        PsiJavaCodeReferenceElement[] referenceElements = psiMethod.getThrowsList().getReferenceElements();
        return referenceElements;
    }

    public static boolean isDirectory(PsiElement psiElement) {
        return psiElement.getContainingFile().isDirectory();
    }

    /**
     * 获取元素的全限定名，如
     * AService有属性B，那么B的全限定名是AService.B，而不是计算B的全限定名
     *
     * @param psiMember
     * @return
     */
    public static String getMemberQualifiedName(PsiMember psiMember) {
        return PsiUtil.getMemberQualifiedName(psiMember);
    }

    /**
     * @param virtualFile
     * @return
     */
    public static List<PsiClass> getPsiClassRecursively(VirtualFile virtualFile) {
        List<PsiClass> psiClasses = new ArrayList<>();
        VfsUtilCore.visitChildrenRecursively(virtualFile, new VirtualFileVisitor<Object>(VirtualFileVisitor.NO_FOLLOW_SYMLINKS) {
            @Override
            public boolean visitFile(@NotNull VirtualFile file) {
                final PsiFile psiFile = psiManager.findFile(file);
                if (psiFile != null) {
                    if (psiFile instanceof PsiJavaFileImpl) {
                        PsiClass[] classes = ((PsiJavaFileImpl) psiFile).getClasses();
                        if (classes != null && classes.length > 0) {
                            for (PsiClass psiClass : classes) {
                                if (isPublic(psiClass)) {
                                    psiClasses.add(psiClass);
                                }
                            }
                        }
                    }
                }
                return true;
            }
        });
        return psiClasses;
    }

}
